<?php

namespace App\Http\Controllers\Api;

use App\Models\UserInviteLog;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Overtrue\EasySms\EasySms;
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
use App\Models\SmsSendLog;
use GuzzleHttp\Client;
use Laravel\Passport\Client as PassportClient;

class AuthenticateController extends Controller
{
    /**
     * 微信登录
     * @param Request $request
     * @return array
     */
    public function loginByWeixin(Request $request)
    {
        $code = $request->code;
        $phone = $request->phone;
        $smsVerifyCode = $request->sms_verify_code;
        $name = $request->name;
        if (!$code)
        {
            return api("缺少code");
        }
        $miniApp = \EasyWeChat::miniProgram();
        $session = $miniApp->auth->session($code);
        if (!$session->openid)
        {
            return api(json_encode($session));
        }
        $user = User::where("openid", $session->openid)->first();
        if (!$user)
        {
            if (!$phone || !$smsVerifyCode || !$name)
            {
                return api(RET_BIND_PHONE, "新用户，请携带手机号、短信验证码和真实姓名一起登录", $request->all());
            }
            $smsLog = SmsSendLog::where("code", $smsVerifyCode)->whereNull('used_at')->isSuccess()->first();
            if (empty($smsLog))
            {
                return api("验证码 $code 无效");
            }
            $now = Carbon::now()->toDateTimeString();
            if ($smsLog->expires_at < $now)
            {
                return api("验证码 $code 已经过期");
            }
            if ($smsLog['phone'] != $phone)
            {
                return api("手机号 $phone 与验证码 $code 不匹配");
            }
            $smsLog->update(['used_at' => $now]);

            //通过手机查找已存在用户
            $user = User::where("phone", $phone)->first();
            if ($user)
            {
                $user->update([
                    'name' => $name,
                    'openid' => $session->openid,
                    'session_key' => $session->session_key,
                ]);
            }
            else
            {
                $user = User::create([
                    'phone' => $phone,
                    'name' => $name,
                    'openid' => $session->openid,
                    'session_key' => $session->session_key,
                ]);
            }

            if ($request->_u && $request->_c)
            {
                $this->insertUserInviteLog($request->_u, $user->id, $request->_c);
            }
        }
        else
        {
            $update = ['session_key' => $session->session_key];
            if (!$user['phone'] && $phone)
            {
                $update['phone'] = $phone;
            }
            if (!$user['name'] && $name)
            {
                $update['name'] = $name;
            }

            $user->update($update);
        }
        //删除旧token
        $oneMonthAgo = Carbon::now()->addMonths(-1)->toDateTimeString();
        $user->tokens()->where("created_at", "<", $oneMonthAgo)->delete();//删除一个月前的token

        $tokenResult = $user->createToken("微信登录");

        $log = sprintf(
            "%s, params: %s, user: %s, access_token: %s",
            __METHOD__, json_encode($request->all(), 336), $user->id, $tokenResult->accessToken
        );
        \Log::info($log);

        $user->auth = [
            'access_token' => $tokenResult->accessToken,
            'expires_at' => (string)$tokenResult->token->expires_at,
        ];

        return api(0, "OK", $user);
    }

    /**
     * 登录时添加邀请记录，绑定关系用于计算佣金
     *
     * @param $uidSendInvite 发出邀请者
     * @param $invitee 被邀请者
     * @param $channel 渠道
     */
    private function insertUserInviteLog($uidSendInvite, $invitee, $channel)
    {
        $log = sprintf("%s, channel: %s, invitee: %s, uidSendInvite: %s", __METHOD__, $channel, $invitee, $uidSendInvite);
        $userSendInvite = User::find($uidSendInvite);
        if (empty($userSendInvite))
        {
            $log .= ", uidSendInvite not exists";
            \Log::info($log);
            return;
        }

        $userSendInvite->inviteLogs()->create([
            'invitee' => $invitee,
            'channel' => $channel,
        ]);
        \Log::info($log);
    }

    /**通过微信授权更新用户信息
     *
     * @param Request $request
     * @return array
     */
    public function updateByWeixin(Request $request)
    {
        $iv = $request->iv;
        $encryptedData = $request->encryptedData;
        if (empty($iv) || empty($encryptedData))
        {
            return api("require iv + encryptedData");
        }
        $user = \Auth::user();
        $miniApp = \EasyWeChat::miniProgram();
        $decryptedData = $miniApp->encryptor->decryptData($user->session_key, $iv, $encryptedData);
        $update = [
            'nickname' => $decryptedData['nickName'],
            'gender' => $decryptedData['gender'],
            'city_code' => $decryptedData['city_code'],
            'province_code' => $decryptedData['province_code'],
            'country_code' => $decryptedData['country_code'],
            'avatar' => $decryptedData['avatarUrl'],
            'unionid' => $decryptedData['unionid'] ?? "",
        ];
        $user->update($update);
        return api(0, "OK", $user);
    }

    /**
     * 获取手机验证码
     *
     * @param Request $request
     * @return array
     */
    public function smsVerifyCode(Request $request)
    {
        $phone = $request->phone;
        if (empty($phone) || !preg_match('/1[\d]{10}/', $phone))
        {
            return api("手机号" . (string)$phone . "不正确");
        }
        $user = \Auth::user();
        $config = config('sms');
        $easySms = new EasySms($config);
        $code = random_int(100000, 999999);
        $logData = [
            'uid' => $user ? $user->id : 0,
            'phone' => $phone,
            'code' => $code,
            'expires_at' => Carbon::now()->addSeconds($config['lifetime'])->toDateTimeString(),
        ];
        try
        {
            $result = $easySms->send($phone, [
                'content'  => '您的验证码为: ' . $code,
                'template' => $config['template_id'],
                'data' => [
                    'code' => $code
                ],
            ]);
            $logData['send_result'] = SmsSendLog::SEND_RESULT_SUCCESS;
            $logData['send_result_info'] = json_encode($result, 336);
            SmsSendLog::create($logData);
            return api(0, "OK", $request->all());
        }
        catch (NoGatewayAvailableException $e)
        {
            $logData['send_result'] = SmsSendLog::SEND_RESULT_FAIL;
            $logData['send_result_info'] = json_encode($e->getResults(), 336);
            SmsSendLog::create($logData);
            return api("获取验证码失败，请稍候重试");
        }

    }

    /**
     * 绑定手机
     *
     * @param Request $request
     * @return array
     */
    public function bindPhone(Request $request)
    {
        $user = \Auth::user();
        $code = $request->code;
        $phone = $request->phone;
        $name = $request->name;

        $smsLog = SmsSendLog::where("code", $code)->where("uid", $user->id)
            ->whereNull('used_at')->isSuccess()->first();
        if (empty($smsLog))
        {
            return api("验证码 $code 无效");
        }
        $now = Carbon::now()->toDateTimeString();
        if ($smsLog->expires_at < $now)
        {
            return api("验证码 $code 已经过期");
        }
        if ($smsLog['phone'] != $phone)
        {
            return api("手机号 $phone 与验证码 $code 不匹配");
        }

        $update = ['phone' => $phone];
        if ($name)
        {
            $update['name'] = $name;
        }

        $user->update($update);

        $smsLog->update(['used_at' => $now]);

        return api(0, "OK", $update);
    }

    /**
     * 账号密码登录，获取token
     *
     * @param UserRequest $request
     * @return array
     */
    public function login(Request $request)
    {
        $data = [
            'grant_type' => 'password',
            'client_id' => $request->get('client_id', config('oauth.client_id')),
            'client_secret' => $request->get('client_secret', config('oauth.client_secret')),
            'username' => $request->username,
            'password' => $request->password,
            'scope' => '',
        ];
        $url = url('/') . "/oauth/token";
        return $this->requestOAuthServer("post", $url, ['form_params' => $data]);
    }

    /**
     * 手机登录
     *
     * @param Request $request
     * @return array
     */
    public function loginByPhone(Request $request)
    {
        $phone = $request->phone;
        $smsVerifyCode = $request->sms_verify_code;
        if (!$phone || !$smsVerifyCode)
        {
            return api("缺少 phone or sms_verify_code");
        }
        $smsLog = SmsSendLog::where("code", $smsVerifyCode)->whereNull('used_at')->isSuccess()->first();
        if (empty($smsLog))
        {
            return api("验证码 $smsVerifyCode 无效");
        }
        $now = Carbon::now()->toDateTimeString();
        if ($smsLog->expires_at < $now)
        {
            return api("验证码 $smsVerifyCode 已经过期");
        }
        if ($smsLog['phone'] != $phone)
        {
            return api("手机号 $phone 与验证码 $smsVerifyCode 不匹配");
        }
        $smsLog->update(['used_at' => $now]);

        $user = User::where("phone", $phone)->first();
        if (!$user)
        {
            $user = User::create([
                'phone' => $phone,
                'nickname' => $phone,
            ]);

            if ($request->_u && $request->_c)
            {
                $this->insertUserInviteLog($request->_u, $user->id, $request->_c);
            }
        }
        //删除旧token
        $oneMonthAgo = Carbon::now()->addMonths(-1)->toDateTimeString();
        $user->tokens()->where("created_at", "<", $oneMonthAgo)->delete();//删除一个月前的token

        $tokenResult = $user->createToken(__METHOD__);

        $log = sprintf(
            "%s, params: %s, user: %s, access_token: %s",
            __METHOD__, json_encode($request->all(), 336), $user->id, $tokenResult->accessToken
        );
        \Log::info($log);

        $user->auth = [
            'access_token' => $tokenResult->accessToken,
            'expires_at' => (string)$tokenResult->token->expires_at,
        ];

        return api(0, "OK", $user);
    }

    /**
     * 刷新token
     *
     * @param Request $request
     * @return array
     */
    public function refreshToken(Request $request)
    {
        $data = [
            'grant_type' => 'refresh_token',
            'client_id' => $request->get('client_id', config('oauth.client_id')),
            'client_secret' => $request->get('client_secret', config('oauth.client_secret')),
            'refresh_token' => $request->refresh_token,
            'scope' => '',
        ];
        $url = url('/') . "/oauth/token";
        //结果码-1001，刷新token失败，这里前端需要进行登录操作了。
        return $this->requestOAuthServer("post", $url, ['form_params' => $data], -1001);
    }

    /**
     * 退出，删除access_token
     *
     * @param Request $request
     * @return array
     */
    public function logout(Request $request)
    {
//        dd(\Auth::user());
        $result = \Auth::user()->tokens()->delete();
        if ($result)
        {
            return api(0, "OK", []);
        }
        else
        {
            return api("删除token失败");
        }
    }

    public function register(UserRequest $request)
    {
        $result = (new UserRepository())->create($request);
        if ($result['ret'] !== 0)
        {
            return $result;
        }
        //注册成功后直接登录
        $request->query->set('username', $request->email);
        return $this->login($request);
    }

    private function requestOAuthServer($method, $url, array $options = [], $failureCode = -1)
    {
        try
        {
            $client = new Client();
            $response = call_user_func_array([$client, $method], [$url, $options]);
            $result = json_decode((string)$response->getBody(), true);
            return api(0, "OK", $result);
        }
        catch (\Exception $e)
        {
            $result = json_decode((string)$e->getResponse()->getBody(), true);
            return api($failureCode, get_class($e) . ': ' . $result['message'], request()->all());
        }
    }


}
